package com.atom.module.mvi.binder

import com.atom.module.mvi.binder.lifecycle.Lifecycle
import com.atom.module.mvi.binder.lifecycle.Lifecycle.Event.BEGIN
import com.atom.module.mvi.binder.lifecycle.Lifecycle.Event.END
import com.atom.module.mvi.binder.middleware.base.Middleware
import com.atom.module.mvi.binder.middleware.wrapWithMiddleware
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.core.Observable.wrap
import io.reactivex.rxjava3.core.ObservableSource
import io.reactivex.rxjava3.core.Scheduler
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.disposables.Disposable
import io.reactivex.rxjava3.functions.Consumer
import io.reactivex.rxjava3.kotlin.plusAssign

open class Binder(
    private val lifecycle: Lifecycle? = null // 一个生命周期对象,实现了可观察对象,事件是start/end
) : Disposable {
    private val disposables = CompositeDisposable()
    private val connections = mutableListOf<Pair<Connection<*, *>, Middleware<*, *>?>>()
    private val connectionDisposables = CompositeDisposable()
    private var isActive = false

    init {
        lifecycle?.let {
            disposables += it.setupConnections()
        }

        disposables += connectionDisposables
    }

    // region bind

    fun <T : Any> bind(connection: Pair<ObservableSource<out T>, Consumer<in T>>) {
        bind(
            Connection(
                from = connection.first,
                to = connection.second,
                connector = null
            )
        )
    }

    fun <Out : Any, In : Any> bind(connection: Connection<Out, In>) {
        val consumer = connection.to
        val middleware = consumer.wrapWithMiddleware(
            standalone = false,
            name = connection.name
        ) as? Middleware<Out, In>
        when {
            lifecycle != null -> {
                connections += (connection to middleware)
                if (isActive) {
                    subscribeWithLifecycle(connection, middleware)
                }
            }
            else -> subscribe(connection, middleware)
        }
    }

    private fun <Out, In> subscribeWithLifecycle(
        connection: Connection<Out, In>,
        middleware: Middleware<Out, In>?
    ) {
        connectionDisposables += wrap(connection.from) // 将from进行包装
            .subscribeWithMiddleware(connection, middleware)
    }

    private fun <Out, In> subscribe(
        connection: Connection<Out, In>,
        middleware: Middleware<Out, In>?
    ) {
        disposables += wrap(connection.from)
            .subscribeWithMiddleware(connection, middleware)
    }

    private fun <Out, In> Observable<out Out>.subscribeWithMiddleware(
        connection: Connection<Out, In>,
        middleware: Middleware<Out, In>?
    ): Disposable = applyTransformer(connection)
        .run {
            if (middleware != null) { // 判断消费consume 不为null
                middleware.onBind(connection)
                this.optionalObserveOn(connection.observeScheduler)
                    .doOnNext { middleware.onElement(connection, it) }
                    .doFinally { middleware.onComplete(connection) }
                    .subscribe(middleware)
            } else {
                optionalObserveOn(connection.observeScheduler)
                    .subscribe(connection.to)
            }
        }

    private fun <Out, In> Observable<out Out>.applyTransformer(
        connection: Connection<Out, In>
    ): Observable<In> =
        connection.connector?.let {
            // 进行 out 转 in 的事件源的转换
            wrap(it.invoke(this))
        } ?: this as Observable<In> // 否则将自身转为in的事件源

    // endregion

    // region lifecycle

    private fun Lifecycle.setupConnections(): Disposable {
        val subscribe: Disposable = wrap(this) // 再一次进行包装
            .distinctUntilChanged() // 过滤掉连续重复的元素,不连续重复的是不过滤
            .subscribe(object : Consumer<Lifecycle.Event> {
                override fun accept(t: Lifecycle.Event) {
                    when (t) {
                        BEGIN -> bindConnections()
                        END -> unbindConnections()
                    }
                }
            })
        return subscribe
    }

    /**
     * 进行连接的绑定
     */
    private fun bindConnections() {
        isActive = true
        connections.forEach { (connection, middleware) ->
            subscribeWithLifecycle(
                connection as Connection<Any, Any>,
                middleware as? Middleware<Any, Any>
            )
        }
    }

    /**
     * 进行连接的解绑
     */
    private fun unbindConnections() {
        isActive = false
        connectionDisposables.clear()
    }

    // endregion

    override fun isDisposed(): Boolean =
        disposables.isDisposed

    override fun dispose() {
        disposables.dispose()
    }
    /**
     * Any binds that occur within [BinderObserveOnScope] will be observed on the provided scheduler
     */
    fun observeOn(scheduler: Scheduler, observeOnScopeFunc: BinderObserveOnScope.() -> Unit) {
        BinderObserveOnScope(this, scheduler).apply(observeOnScopeFunc)
    }
    private fun <T> Observable<T>.optionalObserveOn(scheduler: Scheduler?) =
        if (scheduler != null) {
            observeOn(scheduler)
        } else {
            this
        }

    class BinderObserveOnScope(
        private val binder: Binder,
        private val observeScheduler: Scheduler
    ) {
        fun <T : Any> bind(connection: Pair<ObservableSource<out T>, Consumer<in T>>) {
            binder.bind(
                Connection(
                    from = connection.first,
                    to = connection.second,
                    connector = null,
                    observeScheduler = observeScheduler
                )
            )
        }

        fun <Out : Any, In : Any> bind(connection: Connection<Out, In>) {
            binder.bind(connection.observeOn(observeScheduler))
        }
    }
    fun clear() {
        disposables.clear()
    }
}
